package com.gitee.wsl.common.ui.ext.shape

import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.shape.ZeroCornerSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.PathOperation
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection

/**
 * https://juejin.cn/post/7560992984016535587?searchId=20251017165301EE50B4D535F41272E4A0
 * 一个可自定义各角形状的图形：[Convex]（圆角）、[Concave]（内凹切割）、
 * [Cut]（对角线切割）或 [Sharp]（90度直角）。针对简单情况（矩形或圆角）
 * 进行了优化，并使用路径相减处理复杂的凹角或切割角。
 *
 * @param topStart 左上角的角样式
 * @param topEnd 右上角的角样式
 * @param bottomEnd 右下角的角样式
 * @param bottomStart 左下角的角样式
 */
@Immutable
private class CornerShape(
    private val topStart: Corner,
    private val topEnd: Corner,
    private val bottomEnd: Corner,
    private val bottomStart: Corner,
) : Shape {
    init {
        listOf(topStart, topEnd, bottomEnd, bottomStart).forEach { corner ->
            require(corner.cornerSize.toPx(Size(width = 100f, height = 100f), Density(1f)) >= 0f) {
                "Corner size must be non-negative, but was ${corner.cornerSize} for corner $corner"
            }
        }
    }

    override fun createOutline(
        size: Size,
        layoutDirection: LayoutDirection,
        density: Density,
    ): Outline {
        if (size.width <= 0f || size.height <= 0f) return Outline.Rectangle(Rect.Zero)
        val rect = Rect(0f, 0f, size.width, size.height)
        val corners = listOf(topStart, topEnd, bottomEnd, bottomStart)
        val radii = corners.map { convexCornerRadius(it, density, size) }
        val concaveRadii = corners.map { concaveRadiusInPixels(it, density, size) }
        val cutSizes = corners.map { cutSizeInPixels(it, density, size) }

        // Fast path: rectangle if no corners have size
        if (radii.all { it == CornerRadius.Zero } && concaveRadii.all { it == 0f } && cutSizes.all { it == 0f }) {
            return Outline.Rectangle(rect)
        }

        // Build base path. Always use a RoundRect to handle all corner types correctly.
        val basePath = Path().apply {
            addRoundRect(rect.toRoundRect(radii))
        }

        // Build concave and cut cutouts
        val cutoutPath = Path().apply {
            corners.forEachIndexed { index, corner ->
                val position = CornerPosition.entries[index]
                when (corner) {
                    is Corner.Concave -> {
                        val radius = concaveRadiusInPixels(corner, density, size)
                        if (radius > 0f) {
                            addConcaveOval(position, radius, size, layoutDirection)
                        }
                    }

                    is Corner.Cut -> {
                        val cutSize = cutSizeInPixels(corner, density, size)
                        if (cutSize > 0f) {
                            addCutTriangle(position, cutSize, size, layoutDirection)
                        }
                    }

                    else -> Unit
                }
            }
        }

        // Use generic outline if any cutouts or concave/cut corners exist
        return if (cutoutPath.isEmpty) {
            Outline.Rounded(rect.toRoundRect(radii))
        } else {
            Outline.Generic(Path.combine(PathOperation.Difference, basePath, cutoutPath))
        }
    }

    private fun clampCornerSize(sizeInPx: Float, size: Size): Float = minOf(sizeInPx, size.width / 2, size.height / 2)

    private fun convexCornerRadius(corner: Corner, density: Density, size: Size): CornerRadius = when (corner) {
        is Corner.Rounded -> {
            val radius = clampCornerSize(corner.cornerSize.toPx(size, density), size)
            if (radius > 0f) CornerRadius(radius) else CornerRadius.Zero
        }

        is Corner.Cut, is Corner.Concave, is Corner.Sharp -> CornerRadius.Zero
    }

    private fun concaveRadiusInPixels(corner: Corner, density: Density, size: Size): Float = when (corner) {
        is Corner.Concave -> clampCornerSize(corner.cornerSize.toPx(size, density), size)
        else -> 0f
    }

    private fun cutSizeInPixels(corner: Corner, density: Density, size: Size): Float = when (corner) {
        is Corner.Cut -> clampCornerSize(corner.cornerSize.toPx(size, density), size)
        else -> 0f
    }

    private fun Path.addConcaveOval(
        position: CornerPosition,
        radius: Float,
        size: Size,
        layoutDirection: LayoutDirection,
    ) {
        val (centerX, centerY) = position.getCenter(size, layoutDirection == LayoutDirection.Rtl)
        val ovalRect = Rect(centerX - radius, centerY - radius, centerX + radius, centerY + radius)
        addOval(ovalRect)
    }

    private fun Path.addCutTriangle(
        position: CornerPosition,
        cutSize: Float,
        size: Size,
        layoutDirection: LayoutDirection,
    ) {
        val (cornerX, cornerY) = position.cornerXY(size, layoutDirection == LayoutDirection.Rtl)

        when (position) {
            CornerPosition.TopStart -> {
                moveTo(cornerX, cornerY)
                lineTo(cornerX + cutSize, cornerY)
                lineTo(cornerX, cornerY + cutSize)
                close()
            }

            CornerPosition.TopEnd -> {
                moveTo(cornerX, cornerY)
                lineTo(cornerX - cutSize, cornerY)
                lineTo(cornerX, cornerY + cutSize)
                close()
            }

            CornerPosition.BottomEnd -> {
                moveTo(cornerX, cornerY)
                lineTo(cornerX, cornerY - cutSize)
                lineTo(cornerX - cutSize, cornerY)
                close()
            }

            CornerPosition.BottomStart -> {
                moveTo(cornerX, cornerY)
                lineTo(cornerX + cutSize, cornerY)
                lineTo(cornerX, cornerY - cutSize)
                close()
            }
        }
    }

    private fun CornerPosition.cornerXY(size: Size, isRtl: Boolean): Pair<Float, Float> {
        val x = when (this) {
            CornerPosition.TopStart, CornerPosition.BottomStart -> if (isRtl) size.width else 0f
            CornerPosition.TopEnd, CornerPosition.BottomEnd -> if (isRtl) 0f else size.width
        }
        val y = when (this) {
            CornerPosition.TopStart, CornerPosition.TopEnd -> 0f
            CornerPosition.BottomStart, CornerPosition.BottomEnd -> size.height
        }
        return x to y
    }

    private enum class CornerPosition(val baseX: Float, val baseY: Float) {
        TopStart(0f, 0f), TopEnd(1f, 0f), BottomEnd(1f, 1f), BottomStart(0f, 1f);

        fun getCenter(size: Size, isRtl: Boolean): Pair<Float, Float> {
            val x = if (isRtl) size.width - baseX * size.width else baseX * size.width
            return x to baseY * size.height
        }
    }
}

/**
 * 将一个 [Rect] 转换为 [RoundRect]，使用四个 [CornerRadius] 值的列表，顺序为：
 * topStart, topEnd, bottomEnd, bottomStart。
 */
private fun Rect.toRoundRect(radii: List<CornerRadius>): RoundRect {
    require(radii.size == 4) { "Radii list must contain exactly four elements" }
    return RoundRect(
        rect = this,
        topLeft = radii[0],
        topRight = radii[1],
        bottomRight = radii[2],
        bottomLeft = radii[3],
    )
}

 sealed interface Corner {
     val cornerSize: CornerSize

     data class Concave(override val cornerSize: CornerSize) : Corner
     data class Rounded(override val cornerSize: CornerSize) : Corner
     data class Cut(override val cornerSize: CornerSize) : Corner
     data object Sharp : Corner {
        override val cornerSize: CornerSize = ZeroCornerSize
    }

     companion object {
         fun rounded(size: Dp): Corner = Rounded(CornerSize(size))
         fun cut(size: Dp): Corner = Cut(CornerSize(size))
         fun concave(size: Dp): Corner = Concave(CornerSize(size))
    }
}

@Composable
fun Shapes.cornerShape(
    bottomEnd: Corner = Corner.Rounded(MaterialTheme.shapes.large.bottomEnd),
    bottomStart: Corner = Corner.Rounded(MaterialTheme.shapes.large.bottomStart),
    topEnd: Corner = Corner.Rounded(MaterialTheme.shapes.large.topEnd),
    topStart: Corner = Corner.Rounded(MaterialTheme.shapes.large.topStart),
): Shape {
    val corners = listOf(bottomEnd, bottomStart, topEnd, topStart)
    return when {
        corners.all { corner -> corner is Corner.Sharp } -> RectangleShape

        corners.all { corner -> corner is Corner.Rounded || corner is Corner.Sharp } -> RoundedCornerShape(
            bottomEnd = bottomEnd.cornerSize,
            bottomStart = bottomStart.cornerSize,
            topEnd = topEnd.cornerSize,
            topStart = topStart.cornerSize,
        )

        corners.all { corner -> corner is Corner.Cut || corner is Corner.Sharp } -> CutCornerShape(
            bottomEnd = bottomEnd.cornerSize,
            bottomStart = bottomStart.cornerSize,
            topEnd = topEnd.cornerSize,
            topStart = topStart.cornerSize,
        )

        else -> CornerShape(
            bottomEnd = bottomEnd,
            bottomStart = bottomStart,
            topEnd = topEnd,
            topStart = topStart,
        )
    }
}
